<!DOCTYPE html>
<html>
<head>
  <title>Local View</title>
  <meta name="description" content="In one diagram show the whole tree and in a second diagram show a subset that is logically near a selected node." />
  <!-- Copyright 1998-2016 by Northwoods Software Corporation. -->
  <meta charset="UTF-8">
  <script src="go.js"></script>
  <link href="../assets/css/goSamples.css" rel="stylesheet" type="text/css" />  <!-- you don't need to use this -->
  <script src="goSamples.js"></script>  <!-- this is only for the GoJS Samples framework -->
  <script id="code">
    function init() {
      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myFullDiagram =
        $(go.Diagram, "fullDiagram",  // each diagram refers to its DIV HTML element by id
          {
            initialAutoScale: go.Diagram.UniformToFill,  // automatically scale down to show whole tree
            maxScale: 0.25,
            contentAlignment: go.Spot.Center,  // center the tree in the viewport
            isReadOnly: true,  // don't allow user to change the diagram
            "animationManager.isEnabled": false,
            layout: $(go.TreeLayout,
                      { angle: 90, sorting: go.TreeLayout.SortingAscending }),
            maxSelectionCount: 1,  // only one node may be selected at a time in each diagram
            // when the selection changes, update the myLocalDiagram view
            "ChangedSelection": showLocalOnFullClick
          });

      myLocalDiagram =  // this is very similar to the full Diagram
        $(go.Diagram, "localDiagram",
          {
            autoScale: go.Diagram.UniformToFill,
            contentAlignment: go.Spot.Center,
            isReadOnly: true,
            layout: $(go.TreeLayout,
                      { angle: 90, sorting: go.TreeLayout.SortingAscending }),
            "LayoutCompleted": function(e) {
              var sel = e.diagram.selection.first();
              if (sel !== null) myLocalDiagram.scrollToRect(sel.actualBounds);
            },
            maxSelectionCount: 1,
            // when the selection changes, update the contents of the myLocalDiagram
            "ChangedSelection": showLocalOnLocalClick
          });

      // Define a node template that is shared by both diagrams
      var myNodeTemplate =
        $(go.Node, "Auto",
          { locationSpot: go.Spot.Center },
          new go.Binding("text", "key", go.Binding.toString),  // for sorting
          $(go.Shape, "Rectangle",
            new go.Binding("fill", "color"),
            { stroke: null }),
          $(go.TextBlock,
            { margin: 5 },
            new go.Binding("text", "key", function(k) { return "node" + k; }))
        );
      myFullDiagram.nodeTemplate = myNodeTemplate;
      myLocalDiagram.nodeTemplate = myNodeTemplate;

      // Define a basic link template, not selectable, shared by both diagrams
      var myLinkTemplate =
        $(go.Link,
          { routing: go.Link.Normal, selectable: false },
          $(go.Shape,
            { strokeWidth: 1 })
        );
      myFullDiagram.linkTemplate = myLinkTemplate;
      myLocalDiagram.linkTemplate = myLinkTemplate;

      // Create the full tree diagram
      setupDiagram();

      // Create a part in the background of the full diagram to highlight the selected node
      highlighter =
        $(go.Part, "Auto",
          {
            layerName: "Background",
            selectable: false,
            isInDocumentBounds: false,
            locationSpot: go.Spot.Center
          },
          $(go.Shape, "Ellipse",
            {
              fill: $(go.Brush, "Radial", { 0.0: "yellow", 1.0: "white" }),
              stroke: null,
              desiredSize: new go.Size(400, 400)
            })
          );
      myFullDiagram.add(highlighter);

      // Start by focusing the diagrams on the node at the top of the tree.
      // Wait until the tree has been laid out before selecting the root node.
      myFullDiagram.addDiagramListener("InitialLayoutCompleted", function(e) {
        var node0 = myFullDiagram.findPartForKey(0);
        if (node0 !== null) node0.isSelected = true;
        showLocalOnFullClick();
      });
    }

    // Make the corresponding node in the full diagram to that selected in the local diagram selected,
    // then call showLocalOnFullClick to update the local diagram.
    function showLocalOnLocalClick() {
      var selectedLocal = myLocalDiagram.selection.first();
      if (selectedLocal !== null) {
        // there are two separate Nodes, one for each Diagram, but they share the same key value
        myFullDiagram.select(myFullDiagram.findPartForKey(selectedLocal.data.key));
      }
    }

    function showLocalOnFullClick() {
      var node = myFullDiagram.selection.first();
      if (node !== null) {
        // make sure the selected node is in the viewport
        myFullDiagram.scrollToRect(node.actualBounds);
        // move the large yellow node behind the selected node to highlight it
        highlighter.location = node.location;
        // create a new model for the local Diagram
        var model = new go.TreeModel();
        // add the selected node and its children and grandchildren to the local diagram
        var nearby = node.findTreeParts(3);  // three levels of the (sub)tree
        // add parent and grandparent
        var parent = node.findTreeParentNode();
        if (parent !== null) {
          nearby.add(parent);
          var grandparent = parent.findTreeParentNode();
          if (grandparent !== null) {
            nearby.add(grandparent);
          }
        }
        // create the model using the same node data as in myFullDiagram's model
        nearby.each(function(n) {
            if (n instanceof go.Node) model.addNodeData(n.data);
          });
        myLocalDiagram.model = model;
        // select the node at the diagram's focus
        var selectedLocal = myLocalDiagram.findPartForKey(node.data.key);
        if (selectedLocal !== null) selectedLocal.isSelected = true;
      }
    }

    // Create the tree model containing TOTAL nodes, with each node having a variable number of children
    function setupDiagram(total) {
      if (total === undefined) total = 100;  // default to 100 nodes
      var nodeDataArray = [];
      for (var i = 0; i < total; i++) {
        nodeDataArray.push({
          key: nodeDataArray.length,
          color: go.Brush.randomColor()
        });
      }
      var j = 0;
      for (var i = 1; i < total; i++) {
        var data = nodeDataArray[i];
        data.parent = j;
        if (Math.random() < 0.3) j++;  // this controls the likelihood that there are enough children
      }
      myFullDiagram.model = new go.TreeModel(nodeDataArray);
    }
  </script>
</head>
<body onload="init()">
<div id="sample">
  <div id="fullDiagram" style="height:250px;width:100%;border:1px solid black;margin:2px"></div>
  <div id="localDiagram" style="height:350px;width:100%;border:1px solid black;margin:2px"></div>
  <button onclick="setupDiagram()">Create New Tree</button>
  <p>
  This sample includes two diagrams, the one on top showing a full tree and the one below
  focusing on a specific node in the tree and those nodes that are logically "near" it.
  When the selection changes in either diagram, the lower diagram changes its focus to the selected node.
  To show which node in the full tree is selected,
  a large yellow highlighter part employing a radial <a>Brush</a> is placed in the background layer of the upper diagram behind the selected node.
  The Create New Tree button will randomly generate a new <a>TreeModel</a> to be used by the diagrams.
  </p>
  <p>
  Although it is not demonstrated in this sample,
  one could well use very simple templates for Nodes and for Links in the top Diagram.
  This would make the top Diagram more efficient to construct when there are very many more nodes.
  And one could use more detailed templates in the bottom Diagram,
  where there is more room to show information for each node.
  </p>
</div>
</body>
</html>